---
title: 客户端集成 Nacos
---

下游服务需要先将自己作为“服务注册者”注册到 Nacos ，这个过程叫做“服务注册”；而上游服务作为“服务消费者”，需要到 Nacos 中获取可供调用的下游服务的列表，这个过程就是服务发现。

而服务注册与服务发现都需要集成 Nacos 。

## 客户端集成 Nacos 的流程
要将 Nacos 集成到应用程序，可以按以下步骤进行操作：
### 1. 添加依赖
添加 Nacos 依赖之前，需要在项目顶层 pom 中定义需要用到的 Spring Cloud 和 Spring Cloud Alibaba 的版本。

Spring Boot、Spring Cloud 和 Spring Cloud  Alibaba 三者之间有严格的版本匹配关系

这些匹配关系都可以从 Spring Cloud Alibaba 的官网 [Github wiki页](https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E)中获取。

这里使用最新的 Spring Boot 3 ，与之对应的 Spring Cloud 和 Spring Cloud Alibaba 的版本如下：

![image-20230402113956615](https://img.wkq.pub/pic/image-20230402113956615.png)

使用 Nacos 之前需要先将 上述三个依赖添加到 父工程中。

```pom
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>3.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2022.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2022.0.0.0-RC1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
```

之后就可以在子模块文件中加入 Nacos 的依赖项 spring-cloud-starter-alibaba-nacos-discovery 了。

```xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
```

在添加完依赖项之后，就可以通过配置项开启 Nacos 的服务治理功能了。Nacos 通过自动配置流程（auto configuration）加载配置项并开启服务注册。

### Nacos 自动配置原理

在Spring Cloud 早期版本，需要在启动类上加上 @EnableDiscoveryClient注解开启服务发现功能；而在新版的Spring Cloud 中，通过配置项就可以开启 Nacos 的功能。这是如何做到的呢？ 

这就要从 Spring Framework 的自动配置流程（Auto Configuration ）说起了。

我们将 Nacos 依赖项添加到项目中，同时也引入了 Nacos 自带的自动配置器，比如下面这几个自动配置器就掌管了 Nacos 核心功能的初始化任务。

* **NacosDiscoveryAutoConfiguration**: 服务发现功能的自动配置器；主要做两件事：加载 Nacos 配置项，声明 NacosServiceDiscovery 类用作服务发现；
* **NacosServiceAutoConfiguration**： 声明核心服务治理类 NacosServiceManager，它可以通过 service id、 group 等一系列参数获取已注册的服务列表。
* **NacosServiceRegisterAutoConfiguration**：Nacos 服务注册的自动配置器。

这里以 NacosDiscoveryAutoConfiguration 为例。

```java title='NacosDiscoveryAutoConfiguration'

package com.alibaba.cloud.nacos.discovery;

import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
public class NacosDiscoveryAutoConfiguration {
    public NacosDiscoveryAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public NacosDiscoveryProperties nacosProperties() {
        return new NacosDiscoveryProperties();
    }

    @Bean
    @ConditionalOnMissingBean
    public NacosServiceDiscovery nacosServiceDiscovery(NacosDiscoveryProperties discoveryProperties, NacosServiceManager nacosServiceManager) {
        return new NacosServiceDiscovery(discoveryProperties, nacosServiceManager);
    }
}

```

NacosDiscoveryAutoConfiguration 中声明了两个方法，也就是 nacosProperties 方法和 nacosServiceDiscovery 。

nacosProperties 方法返回了一个 NacosDiscoveryProperties 类，这个类是专门用来读取和封装 Nacos 配置项的类，它的源码如下：

```java title='nacosProperties'

// 定义了配置项读取的路径
@ConfigurationProperties("spring.cloud.nacos.discovery")
public class NacosDiscoveryProperties {
 // 省略类属性
 // 这里定义的类属性和接下来我们要介绍的配置项是一一对应的
}
```

NacosDiscoveryAutoConfiguration 中的另一个方法 nacosServiceDiscovery 声明了一个服务发现的功能类 NacosServiceDiscovery，它的核心方法的源码如下：

```java title ='nacosServiceDiscovery'

public class NacosServiceDiscovery {
   // 封装了Nacos配置项的类
   private NacosDiscoveryProperties discoveryProperties;
   // 另一个自动装配器声明的核心服务治理类
   private NacosServiceManager nacosServiceManager;

   // 根据服务名称获取所有已注册服务
   public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
      String group = discoveryProperties.getGroup();
      List<Instance> instances = namingService().selectInstances(serviceId, group,
            true);
      return hostToServiceInstanceList(instances, serviceId);
   }

   // 返回所有服务的服务名称
   public List<String> getServices() throws NacosException {
      String group = discoveryProperties.getGroup();
      ListView<String> services = namingService().getServicesOfServer(1,
            Integer.MAX_VALUE, group);
      return services.getData();
   }
   // 省略部分代码...
}
```

 通过 NacosServiceDiscovery 暴露的方法，我们就能够根据 serviceId（注册到 nacos 的服务名称）查询到可用的服务实例，获取到服务实例列表之后，调用方就可以发起远程服务调用了。

### 添加 Nacos 配置项

常用的 nacos 配置项如下：

```yaml
spring:
  cloud:
    nacos:
      discovery:
        # Nacos的服务注册地址，可以配置多个，逗号分隔
        server-addr: localhost:8848
        # 服务注册到Nacos上的名称，一般不用配置
        service: service-a
        # nacos客户端向服务端发送心跳的时间间隔，时间单位其实是ms
        heart-beat-interval: 5000
        # 服务端没有接受到客户端心跳请求就将其设为不健康的时间间隔，默认为15s
        # 注：推荐值该值为15s即可，如果有的业务线希望服务下线或者出故障时希望尽快被发现，可以适当减少该值
        heart-beat-timeout: 20000
        # 元数据部分 - 可以自己随便定制
        metadata:
          mydata: abc
        # 客户端在启动时是否读取本地配置项(一个文件)来获取服务列表
        # 注：推荐该值为false，若改成true。则客户端会在本地的一个
        # 文件中保存服务信息，当下次宕机启动时，会优先读取本地的配置对外提供服务。
        naming-load-cache-at-start: false
        # 命名空间ID，Nacos通过不同的命名空间来区分不同的环境，进行数据隔离，
        namespace: dev
        # 创建不同的集群
        cluster-name: Cluster-A
        # [注意]两个服务如果存在上下游调用关系，必须配置相同的group才能发起访问
        group: myGroup
        # 向注册中心注册服务，默认为true
        # 如果只消费服务，不作为服务提供方，倒是可以设置成false，减少开销
        register-enabled: true
```

![bd3383d12b43a35cfc3c240386c3e0f8](https://img.wkq.pub/pic/bd3383d12b43a35cfc3c240386c3e0f8.webp)

Namespace 可以用作环境隔离或者多租户隔离，其中：

* 环境隔离： 比如开发环境、测试环境和生产环境，如果未指定 namespace 则服务将会被注册到 public 这个默认的 namespace 下。
* 多租户隔离： 通过为每一个用户提供独立的 namespace 以实现租户与租户之间的环境隔离。

Group 的使用场景非常灵活：

* **环境隔离**：在多租户架构下，由于 namespace 已经被用于租户隔离，为了实现同一个租户下的环境隔离，可以使用 group 作为环境隔离变量。
* **线上测试**：将上下游的待测试服务的 group 设置为 group-A，线上的用户流量不回导向这个 group。

## 服务提供者发起调用

借助 Nacos 的服务发现功能从注册中心获取可供调用的服务列表，然后才可以发起一个远程调用。

### 添加 Nacos 依赖项和配置信息

```xml

<!-- Nacos服务发现组件 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- 负载均衡组件 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

<!-- webflux服务调用 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>    
<!-- OpenFeign组件 -->
 <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
 </dependency>
```

* spring-cloud-starter-loadbalancer： Spring Cloud 的御用负载均衡组件，用来替代已经进入维护状态的 Ribbon。

## Nacos 服务发现底层实现

Nacos Client 通过一种**主动轮询**的机制从 Nacos Service 获取服务注册信息，包括地址列表、group 分组、cluster 名称等一系列数据。简单来说，Nacos Client 会开启一个本地的定时任务，每间隔一段时间，就尝试从 Nacos Server 查询服务注册表，并将最新的注册信息更新到本地。这种方式也被称为 Pull 模式，即客户端主动从服务端拉取的模式。

负责拉去服务的任务是 UpdateTask 类，它实现了 Runnable 接口。Nacos 以开启线程的方式调用 UpdateTask 类中的 run 方法，触发本地的服务发现查询请求。 

